#pragma once 
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <string>   
#include <functional>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

using namespace LogMoudle;

const static int gsocketfd = -1;
//const static std::string gip = "127.0.0.1"; // 表示本地主机
const static std::string gip = "0.0.0.0"; // 表示本地主机
const static uint16_t gport = 8080;
const static int size = 1024;
// static 全局变量和普通全局变量的区别 ，
// 普通全局变量具有外链接性，这意味着在链接阶段，不同源文件里同名的普通全局变量会被视为同一个变量.
// 在别的源文件中用extern声明就可以使用，在最后链接的时候编译器就会链接上，作用域是整个程序。
// 但static 修饰之后，就具有内链接性了，即不同源文件里同名的静态全局变量是相互独立的，不会相互影响，作用域是当前源文件。
// 这里我们为避免和其他源文件中的同名变量冲突，并且指向让其作用于当前源文件，所以这里采用static 修饰

using func_t = std::function<std::string(const std::string&)>;
// typedef std::function<std::string(const std::string&)> func_t;
// 这两者是等价的，只是不同的写法


class UdpSever
{
public:
    UdpSever(uint16_t port=gport)
        : _socketfd(gsocketfd),
          _addr(port),
          _isrunning(false)
    {
    }
    void Init()
    {
        // 1. 创建socketfd
        _socketfd = ::socket(AF_INET, SOCK_DGRAM, 0);
        if (_socketfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket create error";
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::NORMAL) << "Udp_socket create success socketfd: " << _socketfd;
        // 2.  创建 Sockaddr_in  && bind 绑定
        // struct sockaddr_in local;
        // bzero(&local, sizeof(local));
        // local.sin_family = AF_INET;
        // local.sin_port = ::htons(_port);                  // 这里需要将端口转换为网络字节序，因为端口也是需要通过网络发给对方的。 TODO
        // local.sin_addr.s_addr = ::inet_addr(_ip.c_str()); // 1.string -> uint32_t 2. uint32_t 必须是网络序的，因为ip会在网络上使用
       //  local.sin_addr.s_addr=INADDR_ANY; 
  
    //   if (bind(_socketfd, (struct sockaddr *)&local, sizeof(local))<0)
        if(bind(_socketfd,_addr.Netaddr_ptr(),_addr.Netaddrlen())<0)
        {
            LOG(LogLevel::FATAL) << "bind error,error " << errno << " error string: " << strerror(errno);
            Die(BIND_ERR);
        }
        LOG(LogLevel::NORMAL) << "bind success...";
    }
    void Run(func_t func)
    {
        _isrunning = true;
        while (_isrunning)
        {
            char buffer[size];
            struct sockaddr_in client;
            socklen_t len = sizeof(client); // 必须设置
            ssize_t n = recvfrom(_socketfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr *)&client, &len);
            if (n < 0)
            {
                LOG(LogLevel::WARNNING) << "recvfrom error,error " << errno << "error string: " << strerror(errno);
                continue;
            }
            buffer[n] = 0; // 保证是一个完成的字符串。
            //  输出显示
            InetAddr C(client);
            std::string Clientinfo=C.Ip()+": "+std::to_string(C.Port())+"# "+buffer;
            LOG(LogLevel::NORMAL)<<Clientinfo;

            //  数据处理部分
            //std::string server_info = "Server echo# ";
            std::string server_info=func(buffer);
            // std::cout<<server_info<<std::endl;
            ::sendto(_socketfd, server_info.c_str(), server_info.size(), 0,(struct sockaddr*)&client,len);
        }
    }
    void Stop()
    {
        _isrunning=false;
    }
    ~UdpSever() 
    {
        if(_socketfd>gsocketfd)
            close(_socketfd);
    }
private:
    bool _isrunning;
    int _socketfd;   // 网络socket文件描述符
    // std::string _ip; // 网络IP
    // uint16_t _port;  // 表示与服务器进程绑定的端口
    InetAddr _addr;
};
